package com.yvon.maple.service.auth.service.impl;

import com.yvon.boot.common.result.GlobalException;
import com.yvon.boot.common.result.ResponseData;
import com.yvon.boot.common.result.ResultCode;
import com.yvon.maple.cloud.api.system.dto.SecurityUserDTO;
import com.yvon.maple.cloud.api.system.feign.UserFeignClient;
import com.yvon.maple.cloud.api.system.vo.LdapUserVo;
import com.yvon.maple.service.auth.constant.MessageConstant;
import com.yvon.maple.service.auth.domain.SecurityUser;
import com.yvon.maple.service.auth.model.LdapUser;
import com.yvon.maple.service.auth.service.LdapService;
import com.yvon.maple.service.auth.service.SecurityUserService;
import com.yvon.maple.utils.DataConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * 安全用户service实现类
 *
 * @author : Yvon
 * @since : 2022-02-25
 */
@Service
public class SecurityUserServiceImpl implements SecurityUserService {

    @Resource
    private UserFeignClient userFeignClient;


    @Autowired
    @Lazy
    private LdapService ldapService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
        ResponseData<SecurityUserDTO> result = userFeignClient.getByUsername(username);
        if (!ResponseData.isSuccess(result)) {
            throw new GlobalException(ResultCode.GLOBAL_EXCEPTION);
        }
        SecurityUserDTO dto = result.getData();
        if (Objects.isNull(dto)) {
            throw new GlobalException(ResultCode.JWT_USER_NOT_FOUND);
        }
        SecurityUser securityUser = new SecurityUser(dto, getAuthorities(dto.getRoleCodes()));
        checkUser(securityUser);
        return securityUser;
    }

    @Override
    public UserDetails loadUserByMobile(String mobile) {
        ResponseData<SecurityUserDTO> result = userFeignClient.getByMobile(mobile);
        if (!ResponseData.isSuccess(result)) {
            throw new GlobalException(ResultCode.GLOBAL_EXCEPTION);
        }
        SecurityUserDTO dto = result.getData();
        if (Objects.isNull(dto)) {
            throw new GlobalException(ResultCode.JWT_USER_NOT_FOUND);
        }
        SecurityUser securityUser = new SecurityUser(dto, getAuthorities(dto.getRoleCodes()));
        checkUser(securityUser);
        return securityUser;
    }

    @Override
    public UserDetails loadUserByEmail(String email) {
        ResponseData<SecurityUserDTO> result = userFeignClient.getByEmail(email);
        if (!ResponseData.isSuccess(result)) {
            throw new GlobalException(ResultCode.GLOBAL_EXCEPTION);
        }
        SecurityUserDTO dto = result.getData();
        if (Objects.isNull(dto)) {
            throw new BadCredentialsException(MessageConstant.MOBILE_ERROR);
        }
        SecurityUser securityUser = new SecurityUser(dto, getAuthorities(dto.getRoleCodes()));
        checkUser(securityUser);
        return securityUser;
    }

    @Override
    public UserDetails loadUserByLdap(String username, String password) {
        // 先查询是否存在
        LdapUser ldapUser = ldapService.getUserByUsername(username);
        if (Objects.isNull(ldapUser)) {
            throw new GlobalException(ResultCode.JWT_USER_NOT_FOUND);
        }
        // 校验账号密码
        ldapService.authentication(username, password);
        // 保存到用户表
        userFeignClient.saveLdapUser(DataConverter.convert(ldapUser, LdapUserVo.class));
        ResponseData<SecurityUserDTO> result = userFeignClient.getByUsername(username);
        if (!ResponseData.isSuccess(result)) {
            throw new GlobalException(ResultCode.GLOBAL_EXCEPTION);
        }
        SecurityUserDTO dto = result.getData();
        SecurityUser securityUser = new SecurityUser(dto, getAuthorities(dto.getRoleCodes()));
        checkUser(securityUser);
        return securityUser;
    }


    public void checkUser(SecurityUser securityUser) {
        if (!securityUser.isEnabled()) {
            throw new DisabledException(MessageConstant.ACCOUNT_DISABLED);
        } else if (!securityUser.isAccountNonLocked()) {
            throw new LockedException(MessageConstant.ACCOUNT_LOCKED);
        } else if (!securityUser.isAccountNonExpired()) {
            throw new AccountExpiredException(MessageConstant.ACCOUNT_EXPIRED);
        } else if (!securityUser.isCredentialsNonExpired()) {
            throw new CredentialsExpiredException(MessageConstant.CREDENTIALS_EXPIRED);
        }
    }

    public Collection<? extends GrantedAuthority> getAuthorities(List<String> roleCodes) {
        Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
        if (roleCodes != null) {
            roleCodes.forEach(item -> authorities.add(new SimpleGrantedAuthority(item)));
        }
        return authorities;
    }
}
